package com.ushine.web.template.validation.controller;

import com.ushine.web.component.http.HttpResponseVO;
import com.ushine.web.component.page.QueryVO;
import com.ushine.web.component.validation.ToolInterface;
import com.ushine.web.template.validation.model.ValidationDO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.hibernate.validator.constraints.Length;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @Author: yls
 * @Date 2022/2/11 11:29
 * @Description: 数据校验测试
 * Valid：注解用于校验，所属包为：javax.validation.Valid。@Valid不提供分组功能
 * Validated Validated是@Valid 的一次封装，是Spring提供的校验机制使用。
 * @Version 1.0
 **/
@RestController
@Api(tags = "组件模块-数据校验测试")
@RequestMapping("component/validationView")
@Validated
public class ValidationViewController {
    /**
     * Validated: 表示需要对该参数做检验，紧接着BindingResult参数表示在校验出错时保存的出错信息
     * 如果BindingResult中的hasErrors方法返回true，表示有错误信息，此时遍历错误信息，将返回给前端
     *
     * @param validation    用户
     * @param bindingResult 校验结果
     */
    @ApiOperation(value = "原生校验")
    @PostMapping("user")
    public HttpResponseVO<List<String>> insertUser(@RequestBody @Valid ValidationDO validation, BindingResult bindingResult) {
        List<String> result = getValidResult(bindingResult);
        return new HttpResponseVO<List<String>>().ok("", result);
    }

    /**
     * Validated: 表示需要对该参数做检验，紧接着BindingResult参数表示在校验出错时保存的出错信息
     * 如果BindingResult中的hasErrors方法返回true，表示有错误信息，此时遍历错误信息，将返回给前端
     *
     * @param validation 待校验实体数据
     */
    @ApiOperation(value = "直接校验实体类")
    @PostMapping("/user/allTest")
    public HttpResponseVO<Object> insertUserTest(@RequestBody @Valid ValidationDO validation) {
        return new HttpResponseVO<>().ok("直接校验实体类-验证通过！");
    }


    /**
     * 功能描述:
     * 〈分组校验: update组〉
     *
     * @param validation 待校验的分组
     * @return : com.ushine.web.component.http.HttpResponseVO
     * @author : yls
     * @date : 2022/2/11 11:47
     */
    @ApiOperation(value = "update组校验")
    @PostMapping("/user/update")
    public HttpResponseVO<Object> updateUser(@RequestBody @Validated(ToolInterface.Update.class) ValidationDO validation) {
        return new HttpResponseVO<>().ok("update组校验-验证通过！！！");
    }

    /**
     * 功能描述:
     * 〈分组校验：insert组〉
     *
     * @param validation 待校验的分组
     * @return : com.ushine.web.component.http.HttpResponseVO
     * @author : yls
     * @date : 2022/2/11 11:48
     */
    @ApiOperation(value = "insert组校验")
    @PostMapping("/user/insert")
    public HttpResponseVO<Object> insertUser(@RequestBody @Validated(ToolInterface.Insert.class) ValidationDO validation) {
        return new HttpResponseVO<>().ok("insert组校验-验证通过！！！");
    }


    /**
     * 功能描述:
     * 〈分组校验：all组〉
     *
     * @param validation 待校验的实体
     * @return : com.ushine.web.component.http.HttpResponseVO
     * @author : yls
     * @date : 2022/2/11 11:46
     */
    @ApiOperation(value = "多个组校验")
    @PostMapping("/user/all")
    public HttpResponseVO<Object> allUser(@RequestBody @Validated(ToolInterface.All.class) ValidationDO validation) {
        return new HttpResponseVO<>().ok("多个组校验-验证通过！！！");
    }


    /**
     * 功能描述:
     * 〈当请求参数不是requestBody，而是单个或者多个参数的时候，〉
     *
     * @param name 姓名
     * @return : com.ushine.web.component.http.HttpResponseVO
     * @author : yls
     * @date : 2022/2/11 11:48
     */
    @ApiOperation(value = "单个参数校验")
    @PostMapping("/user/add")
    public HttpResponseVO<Object> insertUser(@RequestParam("name") @Length(min = 4, max = 10, message = "姓名的长度范围（4-10）") String name) {
        return new HttpResponseVO<>().ok("result");
    }


    /**
     * 功能描述:
     * 〈嵌套对象校验，需要在父级对象中需要校验的对象添加注解【@Valid】，才能是子对象进行自动校验〉
     * 例如：
     *  class QueryVO<V,L> {
     * 这个注解非常重要，可以使嵌套对象可以自动进行数据校验
     * 这样【V】对象实体类的属性字段数据校验方可生效，在请求的方法前加【@Valid】即可
     * @param queryVO 嵌套验证对象
     * @return : com.authority.manager.base.http.HttpResponseVO
     * @author : yls
     * @date : 2021/7/15 10:24
     */
    @ApiOperation(value = "嵌套对象校验")
    @PostMapping("/user/nesting")
    public HttpResponseVO<Object> insertUserByNesting(@RequestBody @Valid QueryVO<ValidationDO> queryVO) {
        return new HttpResponseVO<>().ok("嵌套对象校验-验证通过！");
    }


    /**
     * 功能描述:
     * 〈统一返回数据校验的结果〉
     *
     * @param result 返回结果集
     * @return : java.util.List<java.lang.String>
     * @author : yls
     * @date : 2022/03/02 09:57
     */
    private List<String> getValidResult(BindingResult result) {
        List<String> errors = new ArrayList<>();
        if (result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            allErrors.forEach(objectError -> {
                String errorMsg = null;
                errorMsg = new String(Objects.requireNonNull(objectError.getDefaultMessage()).getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
                errors.add(errorMsg);
            });
        }
        return errors;
    }
}
